home *** CD-ROM | disk | FTP | other *** search
/ Chip 2005 August (Alt) / CHIP 2005-08.1.iso / program / guvenlik / syslinux-3.07.exe / extlinux / extlinux.c < prev    next >
Encoding:
C/C++ Source or Header  |  2005-01-12  |  17.6 KB  |  756 lines

  1. #ident "$Id: extlinux.c,v 1.14 2005/01/12 09:56:49 hpa Exp $"
  2. /* ----------------------------------------------------------------------- *
  3.  *   
  4.  *   Copyright 1998-2005 H. Peter Anvin - All Rights Reserved
  5.  *
  6.  *   This program is free software; you can redistribute it and/or modify
  7.  *   it under the terms of the GNU General Public License as published by
  8.  *   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
  9.  *   Boston MA 02111-1307, USA; either version 2 of the License, or
  10.  *   (at your option) any later version; incorporated herein by reference.
  11.  *
  12.  * ----------------------------------------------------------------------- */
  13.  
  14. /*
  15.  * extlinux.c
  16.  *
  17.  * Install the extlinux boot block on an ext2/3 filesystem
  18.  */
  19.  
  20. #define  _GNU_SOURCE        /* Enable everything */
  21. #include <inttypes.h>
  22. /* This is needed to deal with the kernel headers imported into glibc 3.3.3. */
  23. typedef uint64_t u64;
  24. #include <alloca.h>
  25. #include <errno.h>
  26. #include <fcntl.h>
  27. #include <stdio.h>
  28. #include <unistd.h>
  29. #include <mntent.h>
  30. #include <stdlib.h>
  31. #include <string.h>
  32. #include <getopt.h>
  33. #include <sysexits.h>
  34. #include <sys/ioctl.h>
  35. #include <sys/stat.h>
  36. #include <sys/types.h>
  37. #include <sys/mount.h>
  38.  
  39. #include <linux/fd.h>        /* Floppy geometry */
  40. #include <linux/hdreg.h>    /* Hard disk geometry */
  41. #include <linux/fs.h>        /* FIGETBSZ, FIBMAP */
  42.  
  43. #include "ext2_fs.h"
  44. #include "../version.h"
  45.  
  46. #ifdef DEBUG
  47. # define dprintf printf
  48. #else
  49. # define dprintf(...) ((void)0)
  50. #endif
  51.  
  52. /* Global option handling */
  53.  
  54. const char *program;
  55.  
  56. /* These are the options we can set and their values */
  57. struct my_options {
  58.   unsigned int sectors;
  59.   unsigned int heads;
  60. } opt = {
  61.   .sectors = 0,
  62.   .heads = 0,
  63. };
  64.  
  65. static void __attribute__((noreturn)) usage(int rv)
  66. {
  67.   fprintf(stderr,
  68.       "Usage: %s [options] directory\n"
  69.       "  --zip        -z  Force zipdrive geometry (-H 64 -S 32)\n"
  70.       "  --sectors=#  -S  Force the number of sectors per track\n"
  71.       "  --heads=#    -H  Force number of heads\n"
  72.       "\n"
  73.       "  Note: geometry is determined at boot time for devices which\n"
  74.       "  are considered hard disks by the BIOS.  Unfortunately, this is\n"
  75.       "  not possible for devices which are considered floppy disks,\n"
  76.       "  which includes zipdisks and LS-120 superfloppies.\n"
  77.       "\n"
  78.       "  The -z option is useful for USB devices which are considered\n"
  79.       "  hard disks by some BIOSes and zipdrives by other BIOSes.\n",
  80.       program);
  81.  
  82.   exit(rv);
  83. }
  84.  
  85. static const struct option long_options[] = {
  86.   { "zipdrive", 0, NULL, 'z' },
  87.   { "sectors",  1, NULL, 'S' },
  88.   { "heads",    1, NULL, 'H' },
  89.   { "version",  0, NULL, 'v' },
  90.   { "help",     0, NULL, 'h' },
  91.   { 0, 0, 0, 0 }
  92. };
  93.     
  94. static const char short_options[] = "zS:H:vh";
  95.  
  96.  
  97.  
  98. #if defined(__linux__) && !defined(BLKGETSIZE64)
  99. /* This takes a u64, but the size field says size_t.  Someone screwed big. */
  100. # define BLKGETSIZE64 _IOR(0x12,114,size_t)
  101. #endif
  102.  
  103. #define LDLINUX_MAGIC    0x3eb202fe
  104.  
  105. enum bs_offsets {
  106.   bsJump            = 0x00,
  107.   bsOemName         = 0x03,
  108.   bsBytesPerSec     = 0x0b,
  109.   bsSecPerClust     = 0x0d,
  110.   bsResSectors      = 0x0e,
  111.   bsFATs            = 0x10,
  112.   bsRootDirEnts     = 0x11,
  113.   bsSectors         = 0x13,
  114.   bsMedia           = 0x15,
  115.   bsFATsecs         = 0x16,
  116.   bsSecPerTrack     = 0x18,
  117.   bsHeads           = 0x1a,
  118.   bsHiddenSecs      = 0x1c,
  119.   bsHugeSectors     = 0x20,
  120.  
  121.   /* FAT12/16 only */
  122.   bs16DriveNumber   = 0x24,
  123.   bs16Reserved1     = 0x25,
  124.   bs16BootSignature = 0x26,
  125.   bs16VolumeID      = 0x27,
  126.   bs16VolumeLabel   = 0x2b,
  127.   bs16FileSysType   = 0x36,
  128.   bs16Code          = 0x3e,
  129.  
  130.   /* FAT32 only */
  131.   bs32FATSz32       = 36,
  132.   bs32ExtFlags      = 40,
  133.   bs32FSVer         = 42,
  134.   bs32RootClus      = 44,
  135.   bs32FSInfo        = 48,
  136.   bs32BkBootSec     = 50,
  137.   bs32Reserved      = 52,
  138.   bs32DriveNumber   = 64,
  139.   bs32Reserved1     = 65,
  140.   bs32BootSignature = 66,
  141.   bs32VolumeID      = 67,
  142.   bs32VolumeLabel   = 71,
  143.   bs32FileSysType   = 82,
  144.   bs32Code          = 90,
  145.   
  146.   bsSignature     = 0x1fe
  147. };
  148.  
  149. #define bsHead      bsJump
  150. #define bsHeadLen   (bsOemName-bsHead)
  151. #define bsCode        bs32Code    /* The common safe choice */
  152. #define bsCodeLen   (bsSignature-bs32Code)
  153.  
  154. /*
  155.  * Access functions for littleendian numbers, possibly misaligned.
  156.  */
  157. static inline uint8_t get_8(const unsigned char *p)
  158. {
  159.   return *(const uint8_t *)p;
  160. }
  161.  
  162. static inline uint16_t get_16(const unsigned char *p)
  163. {
  164. #if defined(__i386__) || defined(__x86_64__)
  165.   /* Littleendian and unaligned-capable */
  166.   return *(const uint16_t *)p;
  167. #else
  168.   return (uint16_t)p[0] + ((uint16_t)p[1] << 8);
  169. #endif
  170. }
  171.  
  172. static inline uint32_t get_32(const unsigned char *p)
  173. {
  174. #if defined(__i386__) || defined(__x86_64__)
  175.   /* Littleendian and unaligned-capable */
  176.   return *(const uint32_t *)p;
  177. #else
  178.   return (uint32_t)p[0] + ((uint32_t)p[1] << 8) +
  179.     ((uint32_t)p[2] << 16) + ((uint32_t)p[3] << 24);
  180. #endif
  181. }
  182.  
  183. static inline void set_16(unsigned char *p, uint16_t v)
  184. {
  185. #if defined(__i386__) || defined(__x86_64__)
  186.   /* Littleendian and unaligned-capable */
  187.   *(uint16_t *)p = v;
  188. #else
  189.   p[0] = (v & 0xff);
  190.   p[1] = ((v >> 8) & 0xff);
  191. #endif
  192. }
  193.  
  194. static inline void set_32(unsigned char *p, uint32_t v)
  195. {
  196. #if defined(__i386__) || defined(__x86_64__)
  197.   /* Littleendian and unaligned-capable */
  198.   *(uint32_t *)p = v;
  199. #else
  200.   p[0] = (v & 0xff);
  201.   p[1] = ((v >> 8) & 0xff);
  202.   p[2] = ((v >> 16) & 0xff);
  203.   p[3] = ((v >> 24) & 0xff);
  204. #endif
  205. }
  206.  
  207. #ifndef EXT2_SUPER_OFFSET
  208. #define EXT2_SUPER_OFFSET 1024
  209. #endif
  210.  
  211. #define SECTOR_SHIFT    9    /* 512-byte sectors */
  212. #define SECTOR_SIZE    (1 << SECTOR_SHIFT)
  213.  
  214. const char *program;
  215.  
  216. /*
  217.  * Boot block
  218.  */
  219. extern unsigned char extlinux_bootsect[];
  220. extern unsigned int  extlinux_bootsect_len;
  221. #define boot_block    extlinux_bootsect
  222. #define boot_block_len  extlinux_bootsect_len
  223.  
  224. /*
  225.  * Image file
  226.  */
  227. extern unsigned char extlinux_image[];
  228. extern unsigned int  extlinux_image_len;
  229. #define boot_image    extlinux_image
  230. #define boot_image_len  extlinux_image_len
  231.  
  232. /*
  233.  * Common abort function
  234.  */
  235. void __attribute__((noreturn)) die(const char *msg)
  236. {
  237.   fputs(msg, stderr);
  238.   exit(1);
  239. }
  240.  
  241. /*
  242.  * read/write wrapper functions
  243.  */
  244. ssize_t xpread(int fd, void *buf, size_t count, off_t offset)
  245. {
  246.   char *bufp = (char *)buf;
  247.   ssize_t rv;
  248.   ssize_t done = 0;
  249.  
  250.   while ( count ) {
  251.     rv = pread(fd, bufp, count, offset);
  252.     if ( rv == 0 ) {
  253.       die("short read");
  254.     } else if ( rv == -1 ) {
  255.       if ( errno == EINTR ) {
  256.     continue;
  257.       } else {
  258.     die(strerror(errno));
  259.       }
  260.     } else {
  261.       bufp += rv;
  262.       offset += rv;
  263.       done += rv;
  264.       count -= rv;
  265.     }
  266.   }
  267.  
  268.   return done;
  269. }
  270.  
  271. ssize_t xpwrite(int fd, const void *buf, size_t count, off_t offset)
  272. {
  273.   const char *bufp = (const char *)buf;
  274.   ssize_t rv;
  275.   ssize_t done = 0;
  276.  
  277.   while ( count ) {
  278.     rv = pwrite(fd, bufp, count, offset);
  279.     if ( rv == 0 ) {
  280.       die("short write");
  281.     } else if ( rv == -1 ) {
  282.       if ( errno == EINTR ) {
  283.     continue;
  284.       } else {
  285.     die(strerror(errno));
  286.       }
  287.     } else {
  288.       bufp += rv;
  289.       offset += rv;
  290.       done += rv;
  291.       count -= rv;
  292.     }
  293.   }
  294.  
  295.   return done;
  296. }
  297.  
  298. /*
  299.  * Produce file map
  300.  */
  301. int
  302. sectmap(int fd, uint32_t *sectors, int nsectors)
  303. {
  304.   unsigned int blksize, blk, nblk;
  305.   unsigned int i;
  306.  
  307.   /* Get block size */
  308.   if ( ioctl(fd, FIGETBSZ, &blksize) )
  309.     return -1;
  310.  
  311.   /* Number of sectors per block */
  312.   blksize >>= SECTOR_SHIFT;
  313.  
  314.   nblk = 0;
  315.   while ( nsectors ) {
  316.     
  317.     blk = nblk++;
  318.     dprintf("querying block %u\n", blk);
  319.     if ( ioctl(fd, FIBMAP, &blk) )
  320.       return -1;
  321.  
  322.     blk *= blksize;
  323.     for ( i = 0 ; i < blksize ; i++ ) {
  324.       if ( !nsectors )
  325.     return 0;
  326.  
  327.       dprintf("Sector: %10u\n", blk);
  328.       *sectors++ = blk++;
  329.       nsectors--;
  330.     }
  331.   }
  332.  
  333.   return 0;
  334. }
  335.  
  336. /*
  337.  * Get the size of a block device
  338.  */
  339. uint64_t get_size(int devfd)
  340. {
  341.   uint64_t bytes;
  342.   uint32_t sects;
  343.   struct stat st;
  344.  
  345. #ifdef BLKGETSIZE64
  346.   if ( !ioctl(devfd, BLKGETSIZE64, &bytes) )
  347.     return bytes;
  348. #endif
  349.   if ( !ioctl(devfd, BLKGETSIZE, §s) )
  350.     return (uint64_t)sects << 9;
  351.   else if ( !fstat(devfd, &st) && st.st_size )
  352.     return st.st_size;
  353.   else
  354.     return 0;
  355. }
  356.  
  357.  
  358. /*
  359.  * Get device geometry and partition offset
  360.  */
  361. struct geometry_table {
  362.   uint64_t bytes;
  363.   struct hd_geometry g;
  364. };
  365.  
  366. /* Standard floppy disk geometries, plus LS-120.  Zipdisk geometry
  367.    (x/64/32) is the final fallback.  I don't know what LS-240 has
  368.    as its geometry, since I don't have one and don't know anyone that does,
  369.    and Google wasn't helpful... */
  370. static const struct geometry_table standard_geometries[] = {
  371.   {    360*1024, {  2,  9,  40, 0 } },
  372.   {    720*1024, {  2,  9,  80, 0 } },
  373.   {   1200*1024, {  2, 15,  80, 0 } },
  374.   {   1440*1024, {  2, 18,  80, 0 } },
  375.   {   1680*1024, {  2, 21,  80, 0 } },
  376.   {   1722*1024, {  2, 21,  80, 0 } },
  377.   {   2880*1024, {  2, 36,  80, 0 } },
  378.   {   3840*1024, {  2, 48,  80, 0 } },
  379.   { 123264*1024, {  8, 32, 963, 0 } }, /* LS120 */
  380.   { 0, {0,0,0,0} }
  381. };
  382.  
  383. int
  384. get_geometry(int devfd, uint64_t totalbytes, struct hd_geometry *geo)
  385. {
  386.   struct floppy_struct fd_str;
  387.   const struct geometry_table *gp;
  388.  
  389.   memset(geo, 0, sizeof *geo);
  390.  
  391.   if ( !ioctl(devfd, HDIO_GETGEO, &geo) ) {
  392.     return 0;
  393.   } else if ( !ioctl(devfd, FDGETPRM, &fd_str) ) {
  394.     geo->heads     = fd_str.head;
  395.     geo->sectors   = fd_str.sect;
  396.     geo->cylinders = fd_str.track;
  397.     geo->start     = 0;
  398.     return 0;
  399.   } 
  400.  
  401.   /* Didn't work.  Let's see if this is one of the standard geometries */
  402.   for ( gp = standard_geometries ; gp->bytes ; gp++ ) {
  403.     if ( gp->bytes == totalbytes ) {
  404.       memcpy(geo, &gp->g, sizeof *geo);
  405.       return 0;
  406.     }
  407.   }
  408.  
  409.   /* Didn't work either... assign a geometry of 64 heads, 32 sectors; this is
  410.      what zipdisks use, so this would help if someone has a USB key that
  411.      they're booting in USB-ZIP mode. */
  412.  
  413.   geo->heads     = opt.heads ?: 64;
  414.   geo->sectors   = opt.sectors ?: 32;
  415.   geo->cylinders = totalbytes/(geo->heads*geo->sectors << SECTOR_SHIFT);
  416.   geo->start     = 0;
  417.  
  418.   if ( !opt.sectors && !opt.heads )
  419.     fprintf(stderr, "Warning: unable to obtain device geometry (defaulting to %d heads, %d sectors)\n",
  420.         geo->heads, geo->sectors);
  421.  
  422.   return 1;
  423. }
  424.  
  425. /*
  426.  * Query the device geometry and put it into the boot sector.
  427.  * Map the file and put the map in the boot sector and file.
  428.  * Stick the "current directory" inode number into the file.
  429.  */
  430. void
  431. patch_file_and_bootblock(int fd, int dirfd, int devfd)
  432. {
  433.   struct stat dirst;
  434.   struct hd_geometry geo;
  435.   uint32_t *sectp;
  436.   uint64_t totalbytes, totalsectors;
  437.   int nsect;
  438.   unsigned char *p, *patcharea;
  439.   int i, dw;
  440.   uint32_t csum;
  441.  
  442.   if ( fstat(dirfd, &dirst) ) {
  443.     perror("fstat dirfd");
  444.     exit(255);            /* This should never happen */
  445.   }
  446.  
  447.   totalbytes = get_size(devfd);
  448.   get_geometry(devfd, totalbytes, &geo);
  449.  
  450.   if ( opt.heads )
  451.     geo.heads = opt.heads;
  452.   if ( opt.sectors )
  453.     geo.sectors = opt.sectors;
  454.  
  455.   /* Patch this into a fake FAT superblock.  This isn't because
  456.      FAT is a good format in any way, it's because it lets the
  457.      early bootstrap share code with the FAT version. */
  458.   dprintf("heads = %u, sect = %u\n", geo.heads, geo.sectors);
  459.  
  460.   totalsectors = totalbytes >> SECTOR_SHIFT;
  461.   if ( totalsectors >= 65536 ) {
  462.     set_16(boot_block+bsSectors, 0);
  463.   } else {
  464.     set_16(boot_block+bsSectors, totalsectors);
  465.   }
  466.   set_32(boot_block+bsHugeSectors, totalsectors);
  467.  
  468.   set_16(boot_block+bsBytesPerSec, SECTOR_SIZE);
  469.   set_16(boot_block+bsSecPerTrack, geo.sectors);
  470.   set_16(boot_block+bsHeads, geo.heads);
  471.   set_32(boot_block+bsHiddenSecs, geo.start);
  472.  
  473.   /* Construct the boot file */
  474.  
  475.   dprintf("directory inode = %lu\n", (unsigned long) dirst.st_ino);
  476.   nsect = (boot_image_len+SECTOR_SIZE-1) >> SECTOR_SHIFT;
  477.   sectp = alloca(sizeof(uint32_t)*nsect);
  478.   if ( sectmap(fd, sectp, nsect) ) {
  479.     perror("bmap");
  480.     exit(1);
  481.   }
  482.  
  483.   /* First sector need pointer in boot sector */
  484.   set_32(boot_block+0x1F8, *sectp++);
  485.   nsect--;
  486.   
  487.   /* Search for LDLINUX_MAGIC to find the patch area */
  488.   for ( p = boot_image ; get_32(p) != LDLINUX_MAGIC ; p += 4 );
  489.   patcharea = p+8;
  490.   
  491.   /* Set up the totals */
  492.   dw = boot_image_len >> 2; /* COMPLETE dwords! */
  493.   set_16(patcharea, dw);
  494.   set_16(patcharea+2, nsect);    /* Does not include the first sector! */
  495.   set_32(patcharea+8, dirst.st_ino); /* "Current" directory */
  496.   
  497.   /* Set the sector pointers */
  498.   p = patcharea+12;
  499.   
  500.   memset(p, 0, 64*4);
  501.   while ( nsect-- ) {
  502.     set_32(p, *sectp++);
  503.     p += 4;
  504.   }
  505.   
  506.   /* Now produce a checksum */
  507.   set_32(patcharea+4, 0);
  508.  
  509.   csum = LDLINUX_MAGIC;
  510.   for ( i = 0, p = boot_image ; i < dw ; i++, p += 4 )
  511.     csum -= get_32(p);        /* Negative checksum */
  512.   
  513.   set_32(patcharea+4, csum);
  514. }
  515.  
  516. /*
  517.  * Install the boot block on the specified device.
  518.  * Must be run AFTER install_file()!
  519.  */
  520. int
  521. install_bootblock(int fd, const char *device)
  522. {
  523.   struct ext2_super_block sb;
  524.  
  525.   if ( xpread(fd, &sb, sizeof sb, EXT2_SUPER_OFFSET) != sizeof sb ) {
  526.     perror("reading superblock");
  527.     return 1;
  528.   }
  529.  
  530.   if ( sb.s_magic != EXT2_SUPER_MAGIC ) {
  531.     fprintf(stderr, "no ext2/ext3 superblock found on %s\n", device);
  532.     return 1;
  533.   }
  534.   
  535.   if ( xpwrite(fd, boot_block, boot_block_len, 0) != boot_block_len ) {
  536.     perror("writing bootblock");
  537.     return 1;
  538.   }
  539.  
  540.   return 0;
  541. }
  542.  
  543. int
  544. install_file(const char *path, int devfd, struct stat *rst)
  545. {
  546.   char *file;
  547.   int fd = -1, dirfd = -1, flags;
  548.   struct stat st;
  549.  
  550.   asprintf(&file, "%s%sextlinux.sys",
  551.        path,
  552.        path[0] && path[strlen(path)-1] == '/' ? "" : "/");
  553.   if ( !file ) {
  554.     perror(program);
  555.     return 1;
  556.   }
  557.  
  558.   dirfd = open(path, O_RDONLY|O_DIRECTORY);
  559.   if ( dirfd < 0 ) {
  560.     perror(path);
  561.     goto bail;
  562.   }
  563.     
  564.   fd = open(file, O_RDONLY);
  565.   if ( fd < 0 ) {
  566.     if ( errno != ENOENT ) {
  567.       perror(file);
  568.       goto bail;
  569.     }
  570.   } else {
  571.     /* If file exist, remove the immutable flag and set u+w mode */
  572.     if ( !ioctl(fd, EXT2_IOC_GETFLAGS, &flags) ) {
  573.       flags &= ~EXT2_IMMUTABLE_FL;
  574.       ioctl(fd, EXT2_IOC_SETFLAGS, &flags);
  575.     }
  576.     if ( !fstat(fd, &st) ) {
  577.       fchmod(fd, st.st_mode | S_IWUSR);
  578.     }
  579.   }
  580.   close(fd);
  581.  
  582.   fd = open(file, O_WRONLY|O_TRUNC|O_CREAT, S_IRUSR|S_IRGRP|S_IROTH);
  583.   if ( fd < 0 ) {
  584.     perror(file);
  585.     goto bail;
  586.   }
  587.   
  588.   /* Write it the first time */
  589.   if ( xpwrite(fd, boot_image, boot_image_len, 0) != boot_image_len ) {
  590.     fprintf(stderr, "%s: write failure on %s\n", program, file);
  591.     goto bail;
  592.   }
  593.  
  594.   /* Map the file, and patch the initial sector accordingly */
  595.   patch_file_and_bootblock(fd, dirfd, devfd);
  596.  
  597.   /* Write it again - this relies on the file being overwritten in place! */
  598.   if ( xpwrite(fd, boot_image, boot_image_len, 0) != boot_image_len ) {
  599.     fprintf(stderr, "%s: write failure on %s\n", program, file);
  600.     goto bail;
  601.   }
  602.  
  603.   /* Attempt to set immutable flag and remove all write access */
  604.   /* Only set immutable flag if file is owned by root */
  605.   if ( !fstat(fd, &st) ) {
  606.     fchmod(fd, st.st_mode & (S_IRUSR|S_IRGRP|S_IROTH));
  607.     if ( st.st_uid == 0 && !ioctl(fd, EXT2_IOC_GETFLAGS, &flags) ) {
  608.       flags |= EXT2_IMMUTABLE_FL;
  609.       ioctl(fd, EXT2_IOC_SETFLAGS, &flags);
  610.     }
  611.   }
  612.  
  613.   if ( fstat(fd, rst) ) {
  614.     perror(file);
  615.     goto bail;
  616.   }
  617.  
  618.   close(dirfd);
  619.   close(fd);
  620.   return 0;
  621.  
  622.  bail:
  623.   if ( dirfd >= 0 )
  624.     close(dirfd);
  625.   if ( fd >= 0 )
  626.     close(fd);
  627.  
  628.   return 1;
  629. }
  630.  
  631. int
  632. install_loader(const char *path)
  633. {
  634.   struct stat st, dst, fst;
  635.   struct mntent *mnt = NULL;
  636.   int devfd, rv;
  637.   FILE *mtab;
  638.  
  639.   if ( stat(path, &st) || !S_ISDIR(st.st_mode) ) {
  640.     fprintf(stderr, "%s: Not a directory: %s\n", program, path);
  641.     return 1;
  642.   }
  643.   
  644.   devfd = -1;
  645.  
  646.   if ( (mtab = setmntent("/proc/mounts", "r")) ) {
  647.     while ( (mnt = getmntent(mtab)) ) {
  648.       if ( (!strcmp(mnt->mnt_type, "ext2") ||
  649.         !strcmp(mnt->mnt_type, "ext3")) &&
  650.        !stat(mnt->mnt_fsname, &dst) &&
  651.        dst.st_rdev == st.st_dev ) {
  652.     fprintf(stderr, "%s is device %s\n", path, mnt->mnt_fsname);
  653.     if ( (devfd = open(mnt->mnt_fsname, O_RDWR|O_SYNC)) < 0 ) {
  654.       fprintf(stderr, "%s: cannot open device %s\n", program, mnt->mnt_fsname);
  655.       return 1;
  656.     }
  657.     break;
  658.       }
  659.     }
  660.   }
  661.  
  662.   if ( devfd < 0 ) {
  663.     /* Didn't find it in /proc/mounts, try /etc/mtab */
  664.     if ( (mtab = setmntent("/etc/mtab", "r")) ) {
  665.       while ( (mnt = getmntent(mtab)) ) {
  666.     if ( (!strcmp(mnt->mnt_type, "ext2") ||
  667.           !strcmp(mnt->mnt_type, "ext3")) &&
  668.          !stat(mnt->mnt_fsname, &dst) &&
  669.          dst.st_rdev == st.st_dev ) {
  670.       fprintf(stderr, "%s is device %s\n", path, mnt->mnt_fsname);
  671.       if ( (devfd = open(mnt->mnt_fsname, O_RDWR|O_SYNC)) < 0 ) {
  672.         fprintf(stderr, "%s: cannot open device %s\n", program, mnt->mnt_fsname);
  673.         return 1;
  674.       }
  675.       break;
  676.     }
  677.       }
  678.     }
  679.   }
  680.  
  681.   if ( devfd < 0 ) {
  682.     fprintf(stderr, "%s: cannot find device for path %s\n", program, path);
  683.     return 1;
  684.   }
  685.  
  686.   install_file(path, devfd, &fst);
  687.  
  688.   if ( fst.st_dev != st.st_dev ) {
  689.     fprintf(stderr, "%s: file system changed under us - aborting!\n",
  690.         program);
  691.     return 1;
  692.   }
  693.  
  694.   sync();
  695.   rv = install_bootblock(devfd, mnt->mnt_fsname);
  696.   close(devfd);
  697.   sync();
  698.  
  699.   endmntent(mtab);
  700.  
  701.   if ( rv ) return rv;
  702.  
  703.   return 0;
  704. }
  705.  
  706. int
  707. main(int argc, char *argv[])
  708. {
  709.   int o;
  710.   const char *directory;
  711.  
  712.   program = argv[0];
  713.  
  714.   while ( (o = getopt_long(argc, argv, short_options,
  715.                  long_options, NULL)) != EOF ) {
  716.     switch ( o ) {
  717.     case 'z':
  718.       opt.heads = 64;
  719.       opt.sectors = 32;
  720.       break;
  721.     case 'S':
  722.       opt.sectors = strtoul(optarg, NULL, 0);
  723.       if ( opt.sectors < 1 || opt.sectors > 63 ) {
  724.     fprintf(stderr, "%s: invalid number of sectors: %u (must be 1-63)\n",
  725.         program, opt.sectors);
  726.     exit(EX_USAGE);
  727.       }
  728.       break;
  729.     case 'H':
  730.       opt.heads = strtoul(optarg, NULL, 0);
  731.       if ( opt.heads < 1 || opt.heads > 256 ) {
  732.     fprintf(stderr, "%s: invalid number of heads: %u (must be 1-256)\n",
  733.         program, opt.heads);
  734.     exit(EX_USAGE);
  735.       }
  736.       break;
  737.     case 'h':
  738.       usage(0);
  739.       break;
  740.     case 'v':
  741.       fputs("extlinux " VERSION "\n", stderr);
  742.       exit(0);
  743.     default:
  744.       fprintf(stderr, "%s: Unknown option: %c\n", program, optopt);
  745.       exit(EX_USAGE);
  746.     }
  747.   }
  748.  
  749.   directory = argv[optind];
  750.  
  751.   if ( !directory )
  752.     usage(EX_USAGE);
  753.  
  754.   return install_loader(directory);
  755. }
  756.